﻿using PageAdmin.Utils.ORM.PropertyDescriptor;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.ComponentModel.DataAnnotations.Schema;
using System.Linq;
using System.Reflection;
namespace PageAdmin.Utils.ORM
{
    internal class AttributeHelper
    {
        /// <summary>
        /// 静态字典，用于保存表名
        /// </summary>
        private static ConcurrentDictionary<string, string> _TableDictionary;

        /// <summary>
        /// 静态字典，用于保存所有字段映射
        /// </summary>
        private static ConcurrentDictionary<string, IEnumerable<ModelFieldMapped>> _FieldDictionary;

        static AttributeHelper()
        {
            _TableDictionary = new ConcurrentDictionary<string, string>();
            _FieldDictionary = new ConcurrentDictionary<string, IEnumerable<ModelFieldMapped>>();
        }

        /// <summary>
        /// 获取实体表名
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="model"></param>
        /// <returns></returns>
        public static string GetTableName(Type type)
        {
            if (_TableDictionary.ContainsKey(type.FullName))
            {
                return _TableDictionary[type.FullName];
            }
            string tableName = type.Name;
            if (type.IsDefined(typeof(TableAttribute), false))
            {
                var attribute = type.GetCustomAttributes(typeof(TableAttribute),true).FirstOrDefault();
                Type attributeType = attribute.GetType();//获取当前的实例的类型
                tableName = attributeType.GetProperty("Name").GetValue(attribute).ToString();
            }
            _TableDictionary.TryAdd(type.FullName, tableName);//表对应关系加入到缓存集合
            GetModelFieldMappingList(type);//同时添加字段映射
            return tableName;
            
        }

        /// <summary>
        /// 获取实体对应的字段映射字段集合
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="isQuery"></param>
        /// <returns></returns>
        public static IEnumerable<ModelFieldMapped> GetModelFieldMappingList(Type type)
        {
            if (_FieldDictionary.ContainsKey(type.FullName))
            {
                return _FieldDictionary[type.FullName];
            }
            HashSet<ModelFieldMapped> fields = new HashSet<ModelFieldMapped>();
            var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
            foreach (var propertie in properties)
            {
                ModelFieldMapped modelFieldsDescriptor = new ModelFieldMapped();
                var typeName = propertie.PropertyType.Name;
                var propertieName = propertie.Name;
                modelFieldsDescriptor.PropertyName = propertieName;
                modelFieldsDescriptor.PropertyInfo = propertie;
                if(propertieName.Equals("id",StringComparison.OrdinalIgnoreCase))
                {
                    modelFieldsDescriptor.IsPrimaryKey = true;
                }
                else
                {
                    modelFieldsDescriptor.IsPrimaryKey = false;
                }
                if (typeName.StartsWith("IEnumerable",StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("ICollection", StringComparison.OrdinalIgnoreCase) || typeName.StartsWith("IList", StringComparison.OrdinalIgnoreCase))
                {
                    modelFieldsDescriptor.IsIEnumerable = true;
                    modelFieldsDescriptor.PropertyType = propertie.PropertyType.GenericTypeArguments[0];
                }
                else
                {
                    modelFieldsDescriptor.IsIEnumerable = false;
                    modelFieldsDescriptor.PropertyType = propertie.PropertyType;
                }
                modelFieldsDescriptor.ValueType = typeName;
                Type subdataMappedAttributeType = typeof(SubdataAttribute);
                Type relateDataMappedAttributeType = typeof(RelateDataAttribute);
                Type modifyAttributeType = typeof(ModifyAttribute);
                Type attachmentMappedAttributeType = typeof(AttachmentMappedAttribute);
                if (propertie.PropertyType.IsValueType || typeName.Equals("string", StringComparison.OrdinalIgnoreCase))
                {
                    //值类型和字符串才和数据库实际字段建立对应关系。
                    if (!propertie.IsDefined(typeof(NotMappedAttribute)))
                    {
                        modelFieldsDescriptor.IsFieldMapped = true;
                        modelFieldsDescriptor.FieldName = propertieName;
                        if (propertie.IsDefined(typeof(ColumnAttribute)))
                        {
                            object propertyName = GetAttributeValue(propertie, typeof(ColumnAttribute), "Name");
                            if (propertyName != null)
                            {
                                modelFieldsDescriptor.FieldName = (string)propertyName;
                            }
                        }
                        if (propertie.IsDefined(typeof(StringLengthAttribute)))
                        {
                            object propertyMaximumLength = GetAttributeValue(propertie, typeof(StringLengthAttribute), "MaximumLength");
                            if (propertyMaximumLength != null)
                            {
                                modelFieldsDescriptor.MaximumLength = (int)propertyMaximumLength;
                            }
                        }

                        if (propertie.IsDefined(typeof(SearchAttribute)))
                        {
                            object propertySearchType = GetAttributeValue(propertie, typeof(SearchAttribute), "SearchType");
                            object propertyIgnoreEmpty = GetAttributeValue(propertie, typeof(SearchAttribute), "IgnoreEmpty");
                            if (propertySearchType != null)
                            {
                                modelFieldsDescriptor.IsSearchMapped = true;
                                SearchMappedDescriptor searchMappedDescriptor = new SearchMappedDescriptor();
                                searchMappedDescriptor.SearchTypeEnum = (SearchTypeEnum)((int)propertySearchType);
                                searchMappedDescriptor.SearchIgnoreEmpty = (bool)propertyIgnoreEmpty;
                                modelFieldsDescriptor.SearchMappedDescriptor = searchMappedDescriptor;
 
                            }
                        }
                        if (propertie.IsDefined(modifyAttributeType))
                        {
                            GetModifyConfig(propertie, modifyAttributeType, modelFieldsDescriptor);
                        }
                    }

                    //附件特性只能用于字符串
                    if (propertie.IsDefined(attachmentMappedAttributeType) && typeName.Equals("string", StringComparison.OrdinalIgnoreCase))
                    {
                        GetAttachmentMapping(properties,propertie, attachmentMappedAttributeType, modelFieldsDescriptor);
                    }                 
                }
                else if (propertie.IsDefined(subdataMappedAttributeType))
                {
                    GetSubdataMapping(propertie, subdataMappedAttributeType, modelFieldsDescriptor);
                }
                else if (propertie.IsDefined(relateDataMappedAttributeType))
                {
                    GetRelateDataMapping(properties,propertie, relateDataMappedAttributeType, modelFieldsDescriptor);
                }
                fields.Add(modelFieldsDescriptor);
            }
            //需要判断，避免多线程报错
            if (!_FieldDictionary.ContainsKey(type.FullName))
            {
                _FieldDictionary.TryAdd(type.FullName, fields);//表对应关系加入到缓存集合
            }
            return fields;
        }

        /// <summary>
        /// 获取附件表映射配置
        /// </summary>
        /// <param name="propertie"></param>
        /// <param name="attachmentMappedAttributeType"></param>
        /// <param name="modelFieldsDescriptor"></param>
        private static void GetAttachmentMapping(PropertyInfo[] properties,PropertyInfo propertie, Type attachmentMappedAttributeType, ModelFieldMapped modelFieldsDescriptor)
        {
            object propertyFieldName = GetAttributeValue(propertie, attachmentMappedAttributeType, "FieldName");
            object propertyFileListPropertyName = GetAttributeValue(propertie, attachmentMappedAttributeType, "FileListPropertyName");
            object propertyAttachmentFileListField = GetAttributeValue(propertie, attachmentMappedAttributeType, "FileListField");
            object propertyListSyncLoad = GetAttributeValue(propertie, attachmentMappedAttributeType, "ListSyncLoad");
            object propertyDetailSyncLoad = GetAttributeValue(propertie, attachmentMappedAttributeType, "DetailSyncLoad");
            if (propertyFieldName != null)
            {
                modelFieldsDescriptor.IsAttachmentMapped = true;
                AttachmentMappedDescriptor attachmentMappedDescriptor = new AttachmentMappedDescriptor();
                attachmentMappedDescriptor.AttachmentFieldName = (string)propertyFieldName;
                string fileListPropertyName = (string)propertyFileListPropertyName;
                if(!string.IsNullOrEmpty(fileListPropertyName))
                {
                   attachmentMappedDescriptor.FileListPropertyName = (string)propertyFileListPropertyName;
                   attachmentMappedDescriptor.FileListPropertyInfo = properties.Where(c => c.Name.Equals(fileListPropertyName, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
                }
                attachmentMappedDescriptor.AttachmentFileListField = (string)propertyAttachmentFileListField;
                attachmentMappedDescriptor.ListSyncLoad = (bool)propertyListSyncLoad;
                attachmentMappedDescriptor.DetailSyncLoad = (bool)propertyDetailSyncLoad;
                modelFieldsDescriptor.AttachmentMappedDescriptor = attachmentMappedDescriptor;
            }
        }

        /// <summary>
        /// 获取子数据表映射配置
        /// </summary>
        /// <param name="propertie"></param>
        /// <param name="subdataMappedAttributeType"></param>
        /// <param name="modelFieldsDescriptor"></param>
        private static void GetSubdataMapping(PropertyInfo propertie, Type subdataMappedAttributeType, ModelFieldMapped modelFieldsDescriptor)
        {
            object propertyForeignKey = GetAttributeValue(propertie, subdataMappedAttributeType, "ForeignKey");
            object propertyAutoAdd = GetAttributeValue(propertie, subdataMappedAttributeType, "SyncAdd");
            object propertyAutoDelete = GetAttributeValue(propertie, subdataMappedAttributeType, "SyncDelete");
            object propertyAutoEdit = GetAttributeValue(propertie, subdataMappedAttributeType, "SyncEdit");
            object propertyDetailAutoLoad = GetAttributeValue(propertie, subdataMappedAttributeType, "DetailSyncLoad");
            object propertyListAutoLoad = GetAttributeValue(propertie, subdataMappedAttributeType, "ListSyncLoad");
            object propertySqlCondition = GetAttributeValue(propertie, subdataMappedAttributeType, "SqlCondition");
            object propertyOrderBy = GetAttributeValue(propertie, subdataMappedAttributeType, "OrderBy");
            if (propertyForeignKey != null)
            {
                modelFieldsDescriptor.IsSubDataMapped = true;
                SubdataMappedDescriptor subdataMappedDescriptor = new SubdataMappedDescriptor();
                subdataMappedDescriptor.ForeignKey = (string)propertyForeignKey;
                subdataMappedDescriptor.SubmodelType = modelFieldsDescriptor.PropertyType;
                subdataMappedDescriptor.PrimaryKeyPropertyInfo= modelFieldsDescriptor.PropertyType.GetProperty("Id", BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                subdataMappedDescriptor.ForeignKeyPropertyInfo = modelFieldsDescriptor.PropertyType.GetProperty(subdataMappedDescriptor.ForeignKey,BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
                subdataMappedDescriptor.SubtableName = GetTableName(modelFieldsDescriptor.PropertyType);
                subdataMappedDescriptor.SyncAdd = (bool)propertyAutoAdd;
                subdataMappedDescriptor.SyncDelete = (bool)propertyAutoDelete;
                subdataMappedDescriptor.SyncEdit = (bool)propertyAutoEdit;
                subdataMappedDescriptor.DetailSyncLoad = (bool)propertyDetailAutoLoad;
                subdataMappedDescriptor.ListSyncLoad = (bool)propertyListAutoLoad;
                subdataMappedDescriptor.SqlCondition = (string)propertySqlCondition;
                subdataMappedDescriptor.OrderBy = (string)propertyOrderBy;
                modelFieldsDescriptor.SubdataMappedDescriptor = subdataMappedDescriptor;
            }
        }

        /// <summary>
        /// 获取关联表映射配置
        /// </summary>
        /// <param name="propertie"></param>
        /// <param name="relateDataAttributeType"></param>
        /// <param name="modelFieldsDescriptor"></param>
        private static void GetRelateDataMapping(PropertyInfo[] properties, PropertyInfo propertie, Type relateDataAttributeType, ModelFieldMapped modelFieldsDescriptor)
        {
            object propertyValueKey = GetAttributeValue(propertie, relateDataAttributeType, "ValueKey");
            object propertyRelateKey = GetAttributeValue(propertie, relateDataAttributeType, "RelateKey");
            object propertySqlCondition = GetAttributeValue(propertie, relateDataAttributeType, "SqlCondition");
            object propertyAutoEdit = GetAttributeValue(propertie, relateDataAttributeType, "OrderBy");
            object propertyDetailAutoLoad = GetAttributeValue(propertie, relateDataAttributeType, "DetailSyncLoad");
            object propertyListAutoLoad = GetAttributeValue(propertie, relateDataAttributeType, "ListSyncLoad");
            string valueKey = (string)propertyValueKey;
            var valueKeyPropertyInfo= properties.Where(c => c.Name.Equals(valueKey, StringComparison.OrdinalIgnoreCase)).FirstOrDefault();
            if(valueKeyPropertyInfo==null)
            {
                return;
            }
            if (propertyValueKey != null && !string.IsNullOrEmpty(valueKey))
            {
                modelFieldsDescriptor.IsRelateDataMapped = true;
                RelateDataMappedDescriptor relateDataMappedDescriptor = new RelateDataMappedDescriptor();
                relateDataMappedDescriptor.RelateModelType= modelFieldsDescriptor.PropertyType;
                relateDataMappedDescriptor.ValueKey = valueKey;
                relateDataMappedDescriptor.RelateKey = (string)propertyRelateKey;
                relateDataMappedDescriptor.ValueKeyPropertyInfo= valueKeyPropertyInfo;
                relateDataMappedDescriptor.SqlCondition = (string)propertySqlCondition;
                relateDataMappedDescriptor.OrderBy = (string)propertyAutoEdit;
                relateDataMappedDescriptor.DetailSyncLoad = (bool)propertyDetailAutoLoad;
                relateDataMappedDescriptor.ListSyncLoad = (bool)propertyListAutoLoad;
                modelFieldsDescriptor.RelateDataMappedDescriptor = relateDataMappedDescriptor;
            }
        }


        /// <summary>
        /// 获取更新数据时的权限配置
        /// </summary>
        /// <param name="propertie"></param>
        /// <param name="subdataMappedAttributeType"></param>
        /// <param name="modelFieldsDescriptor"></param>
        private static void GetModifyConfig(PropertyInfo propertie, Type modifyAttributeType, ModelFieldMapped modelFieldsDescriptor)
        {
            object propertyAllowAdd = GetAttributeValue(propertie, modifyAttributeType, "AllowAdd");
            object propertyAllowEdit = GetAttributeValue(propertie, modifyAttributeType, "AllowEdit");
            if (propertyAllowAdd!= null)
            {
                modelFieldsDescriptor.AllowAdd = (bool)propertyAllowAdd;
                modelFieldsDescriptor.AllowEdit = (bool)propertyAllowEdit;
            }
        }
        /// <summary>
        /// 获取实体类属性上的特性值
        /// </summary>
        /// <param name="property">实体属性</param>
        /// <param name="attrbuteType">特性的类型</param>
        /// <param name="attrbuteAttrName">特性中的属性名称</param>
        /// <returns></returns>
        private static object GetAttributeValue(PropertyInfo property, Type attrbuteType, string attrbuteAttrName)
        {
            var attribute = property.GetCustomAttributes(attrbuteType, true).FirstOrDefault();
            Type attributeType = attribute.GetType();//获取当前的实例的类型
            PropertyInfo propertyInfo = attributeType.GetProperty(attrbuteAttrName);
            object propertyValue = propertyInfo.GetValue(attribute);
            return propertyValue;
        }


    }

}
